package blue.mqtt4j.core.lexer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jin Zheng
 * @since 1.0 2020-10-20
 */
public class SimpleLexer
{
	private static Logger logger = LoggerFactory.getLogger(SimpleLexer.class);

	private List<Token> tokenList;
	private StringBuilder tokenText;
	private TokenType type;

	public SimpleLexer()
	{
	}

	public TokenReader tokenize(String code)
	{
		tokenList = new ArrayList<>();
		if (code == null || code.isEmpty())
			return new SimpleTokenReader(tokenList);

		tokenText = new StringBuilder();
		TokenState state = TokenState.INITIAL;
		char[] chars = code.toCharArray();
		char ch = 0;
		for (var i = 0; i < chars.length; i++)
		{
			ch = chars[i];
			switch (state)
			{
				case INITIAL:
				case GE:
				case ASSIGNMENT:
				case PLUS:
				case MINUS:
				case STAR:
				case SLASH:
				case SEMICOLON:
				case LEFT_PAREN:
				case RIGHT_PAREN:
					state = this.initToken(ch);
					break;
				case IDENTIFIER:
					if (TextUtil.isLetter(ch) || TextUtil.isDigit(ch))
					{
						tokenText.append(ch);
					}
					else
					{
						state = this.initToken(ch);
					}
					break;
				case IDENTIFIER_INT1:
					if (ch == 'n')
					{
						state = TokenState.IDENTIFIER_INT2;
						tokenText.append(ch);
					}
					else if (TextUtil.isLetter(ch) || TextUtil.isDigit(ch))
					{
						state = TokenState.IDENTIFIER;
						tokenText.append(ch);
					}
					else
					{
						state = this.initToken(ch);
					}
					break;
				case IDENTIFIER_INT2:
					if (ch == 't')
					{
						state = TokenState.IDENTIFIER_INT3;
						tokenText.append(ch);
					}
					else if (TextUtil.isLetter(ch) || TextUtil.isDigit(ch))
					{
						state = TokenState.IDENTIFIER;
						tokenText.append(ch);
					}
					else
					{
						state = this.initToken(ch);
					}
					break;
				case IDENTIFIER_INT3:
					if (TextUtil.isBlank(ch))
					{
						type = TokenType.INT;
						state = this.initToken(ch);
					}
					else
					{
						state = TokenState.IDENTIFIER;
						tokenText.append(ch);
					}
					break;
				case GT:
					if (ch == '=')
					{
						state = TokenState.GE;
						type = TokenType.GE;
						tokenText.append(ch);
					}
					else
					{
						state = this.initToken(ch);
					}
					break;
				case INT_LITERAL:
					if (TextUtil.isDigit(ch))
					{
						tokenText.append(ch);
					}
					else
					{
						state = this.initToken(ch);
					}
					break;
			}
		}
		if (tokenText.length() > 0)
		{
			this.initToken(ch);
		}

		return new SimpleTokenReader(tokenList);
	}

	private TokenState initToken(char ch)
	{
		if (tokenText.length() > 0)
		{
			tokenList.add(new SimpleToken(type, tokenText.toString()));

			tokenText = new StringBuilder();
			type = null;
		}
		TokenState state = TokenState.INITIAL;
		if (TextUtil.isLetter(ch))
		{
			if (ch == 'i')
			{
				state = TokenState.IDENTIFIER_INT1;
			}
			else
			{
				state = TokenState.IDENTIFIER;
			}
			type = TokenType.IDENTIFIER;
			tokenText.append(ch);
		}
		else if (TextUtil.isDigit(ch))
		{
			state = TokenState.INT_LITERAL;
			type = TokenType.INT_LITERAL;
			tokenText.append(ch);
		}
		else if (ch == '=')
		{
			state = TokenState.ASSIGNMENT;
			type = TokenType.ASSIGNMENT;
			tokenText.append(ch);
		}
		else if (ch == '>')
		{
			state = TokenState.GT;
			type = TokenType.GT;
			tokenText.append(ch);
		}
		else if (ch == '+')
		{
			state = TokenState.PLUS;
			type = TokenType.PLUS;
			tokenText.append(ch);
		}
		else if (ch == '-')
		{
			state = TokenState.MINUS;
			type = TokenType.MINUS;
			tokenText.append(ch);
		}
		else if (ch == '*')
		{
			state = TokenState.STAR;
			type = TokenType.STAR;
			tokenText.append(ch);
		}
		else if (ch == '/')
		{
			state = TokenState.SLASH;
			type = TokenType.SLASH;
			tokenText.append(ch);
		}
		else if (ch == ';')
		{
			state = TokenState.SEMICOLON;
			type = TokenType.SEMICOLON;
			tokenText.append(ch);
		}
		else if (ch == '(')
		{
			state = TokenState.LEFT_PAREN;
			type = TokenType.LEFT_PAREN;
			tokenText.append(ch);
		}
		else if (ch == ')')
		{
			state = TokenState.RIGHT_PAREN;
			type = TokenType.RIGHT_PAREN;
			tokenText.append(ch);
		}

		return state;
	}

}
